﻿Option Strict On
Option Explicit On
Option Infer On

Imports System
Imports System.Linq
Imports System.Collections.Generic
Imports System.Xml.Linq
Imports GenDotNet.Task.Common
Imports GenDotNet.TemplateSupport.Strings
Imports GenDotNet.ObjectModel
Imports <xmlns:orm="http://kadgen.com/KADORM.xsd">

Namespace CSharp
   Public MustInherit Class DotNetLanguageBase
      Inherits DotNetBase

      Public Sub New()
         MyBase.New()
      End Sub

      Public Overrides Function CleanOutput(ByVal code As String) As String
         Return code.CleanOutput()
      End Function

#Region "Namespace dependent redirects"
      Public Overrides Function ClassOutputClose(ByVal info As TypeInfo) As String
         Return info.OutputClose
      End Function

      Public Overrides Function ClassOutputOpening(ByVal info As TypeInfo) As String
         Return info.OutputOpening()
      End Function

      Public Overrides Function CommentOutput(ByVal info As CommentInfo) As String
         Return info.Output()
      End Function

      Public Overrides Function ConstructorOutput(ByVal info As ConstructorInfo) As String
         Return info.Output()
      End Function

      Public Overrides Function FieldOutput(ByVal info As VariableInfo) As String
         Return info.Output()
      End Function

      Public Overrides Function FileImportsOutput(ByVal fileImports As ImportInfoCollection) As String
         Return fileImports.Output()
      End Function

      Public Overrides Function FileNamespaceOutput(ByVal info As NamespaceInfo) As String
         Return info.Output()
      End Function

      Public Overrides Function FunctionOutput(ByVal info As FunctionInfo) As String
         Return info.Output()
      End Function

      Public Overrides Function GenericCollectionOutput(ByVal infoCollection As GenericInfoCollection) As String
         Return infoCollection.Output()
      End Function

      Public Overrides Function GenericOutput(ByVal info As GenericInfo) As String
         Return info.Output()
      End Function

      Public Overrides Function PropertyOutput(ByVal info As PropertyInfo) As String
         Return info.Output()
      End Function

      Public Overrides Function RegionOutput(ByVal info As RegionInfo) As String
         Return info.Output()
      End Function

      Public Overrides Function SubOutput(ByVal info As SubInfo) As String
         Return info.Output()
      End Function

      Public Overrides Function VariableOutput(ByVal info As VariableInfo) As String
         Return info.Output()
      End Function
#End Region

      Public Overrides Function OutputFileName(ByVal partialName As String) As String
         Return partialName & ".cs"
      End Function

      Public Overrides Sub SetStrings()
         Strings.importsString = "using "
         Strings.wrapString = Strings.nl
         Strings.returnString = "return"
         Strings.newString = "new"
         Strings.andAlsoString = "&&"
         Strings.andString = "&"
         Strings.orElseString = "||"
         Strings.orString = "|"
         Strings.meString = "this"
         Strings.trueString = "true"
         Strings.falseString = "false"
         Strings.notString = "!"
         Strings.nullString = "null"
         Strings.throwString = "throw"
         Strings.baseString = "base"
         Strings.getTypeString = "typeof"
         Strings.semiColonString = ";"
      End Sub

      Public Overrides Function BitwiseCombine(ByVal var As String, ByVal literal As UInt64) As String
         ' C# does an implicit typecast if the literal is in range
         Return _
<code><%= var %> | <%= literal %></code>.Value
      End Function

      Public Overrides Function BitwiseCombine(ByVal var As String, ByVal literal As Int64) As String
         ' C# does an implicit typecast if the literal is in range
         Return _
<code><%= var %> | <%= literal %></code>.Value
      End Function

      Public Overrides Function BitwiseContains(ByVal var As String, ByVal literal As UInt64) As String
         Return BitwiseContains(var, literal.ToString)
      End Function

      Public Overrides Function BitwiseContains(ByVal var As String, ByVal literal As Long) As String
         Return BitwiseContains(var, literal.ToString)
      End Function

      Private Overloads Function BitwiseContains(ByVal var As String, ByVal literal As String) As String
         Return _
         <code>(<%= var %> &amp; <%= literal %>) == <%= literal %></code>.Value
      End Function

      Public Overloads Overrides Function WithSemiColons(ByVal code As IEnumerable(Of System.Xml.Linq.XElement)) As String
         Return WithSemiColons(code.Value)
      End Function

      Public Overloads Overrides Function WithSemiColons(ByVal code As XElement) As String
         Return WithSemiColons(code.Value)
      End Function

      Public Overloads Overrides Function WithSemiColons(ByVal code As String) As String
         Dim lines = code.Split(Convert.ToChar(Strings.lf))
         Dim logicalLine = String.Empty
         Dim ret = String.Empty
         For Each line In lines
            logicalLine &= line
            If line.Trim.EndsWith("_") Then
               logicalLine &= nl
            Else
               ret &= AddSemiIfNeeded(logicalLine)
               logicalLine = String.Empty
            End If
         Next
         Return ret
      End Function

      Private Function AddSemiIfNeeded(ByVal line As String) As String
         Dim ret = String.Empty
         If String.IsNullOrEmpty(line) OrElse line.Trim.Length = 0 OrElse _
                  (line.TrimStart.StartsWith("if (") Or _
                   line.TrimStart.StartsWith("if(") Or _
                   line.TrimStart.StartsWith("foreach (") Or _
                   line.TrimStart.StartsWith("foreach(") Or _
                   line.Trim = "{" Or line.Trim = "}" Or _
                   line.Trim.EndsWith("_")) Then
            ret &= line & Strings.nl
         Else
            If Not line.TrimEnd.EndsWith(";") Then
               ret &= line & ";" & Strings.nl
            End If
         End If
         Return ret
      End Function

      Protected Overrides Sub _DoNotDeriveFromDotNetBaseDeriveFromDotNetLanguageBase()
         ' Dummy code
      End Sub

   End Class

   Public Module LanguageExtensions
      <Runtime.CompilerServices.Extension()> _
     Public Function OutputField( _
        ByVal prop As IPropertyData) _
        As String
         ' Assume private for fields
         Return New VariableInfo(prop.Name, Scope.Private, prop.NetType, prop.DefaultValue).Output
      End Function

      <Runtime.CompilerServices.Extension()> _
      Public Function OutputField( _
         ByVal child As IChildData) _
         As String
         ' Assume private for fields
         Return New VariableInfo(child.Name, Scope.Private, StandardNetType.Unknown, True).Output
      End Function

      <Runtime.CompilerServices.Extension()> _
      Public Function CleanOutput(ByVal out As String) As String
         out = out.Replace(" _", "")
         Dim lines = out.Split(Strings.lf)
         Dim ret = String.Empty
         For Each line In lines
            Dim content = If(line.Contains("//"), line.SubstringBefore("//"), line)
            Dim comments = If(line.Contains("//"), line.SubstringAfter("//"), String.Empty)
            'content = AddSemis(content)
            content = FixCase(content)
            line = content & If(String.IsNullOrEmpty(comments), String.Empty, "//" & comments)
            ret &= line & Strings.lf
         Next
         Return ret
      End Function



#Region "Private helper methods"

      Private Function FixCase(ByVal content As String) As String
         Dim ret = content
         ret = FixSpecificCase(ret, "New", "new", True, True)
         ret = FixSpecificCase(ret, "Return", "return", False, True)
         ret = FixSpecificCase(ret, "True", "true", True, False)
         ret = FixSpecificCase(ret, "False", "false", True, False)
         Return ret
      End Function

      Private Function FixSpecificCase( _
               ByVal ret As String, _
               ByVal badCase As String, _
               ByVal goodCase As String, _
               ByVal allowPreceding As Boolean, _
               ByVal forceTrailingSpace As Boolean) As String
         If ret.Contains(badCase) Then
            ' so it might need replacement, but we need to not replace in symbols
            Dim leading = ret.SubstringBefore(badCase)
            If leading.Trim.Length = 0 Then
               ret = ret.TrimStart
               If ret.StartsWith(badCase) Then
                  If ret.Length = badCase.Length OrElse " ),".Contains(ret.Substring(badCase.Length, 1)) Then
                     ret = goodCase & ret.Substring(badCase.Length)
                  End If
                  ret = leading & ret
               End If
            End If
            If ret.Contains(badCase) Then
               If allowPreceding Then
                  ret = ret.Replace(" " & badCase & " ", " " & goodCase & " ")
                  ret = ret.Replace("(" & badCase & " ", "(" & goodCase & " ")
                  ret = ret.Replace("," & badCase & " ", "," & goodCase & " ")
                  If Not forceTrailingSpace Then
                     ret = ret.Replace(" " & badCase & ")", " " & goodCase & ")")
                     ret = ret.Replace("(" & badCase & ")", "(" & goodCase & ")")
                     ret = ret.Replace("," & badCase & ")", "," & goodCase & ")")
                     ret = ret.Replace(" " & badCase & ",", " " & goodCase & ",")
                     ret = ret.Replace("(" & badCase & ",", "(" & goodCase & ",")
                     ret = ret.Replace("," & badCase & ",", "," & goodCase & ",")
                  End If
               End If
            End If
         End If
         Return ret
      End Function

      Public Function WithSemiColons(ByVal code As String) As String
         Dim lines = code.Split(Convert.ToChar(Strings.lf))
         Dim logicalLine = String.Empty
         Dim ret = String.Empty
         For Each line In lines
            logicalLine &= line
            If line.Trim.EndsWith("_") Then
               logicalLine &= nl
            Else
               ret &= AddSemiIfNeeded(logicalLine)
               logicalLine = String.Empty
            End If
         Next
         Return ret
      End Function

      Private Function AddSemiIfNeeded(ByVal line As String) As String
         Dim ret = String.Empty
         If String.IsNullOrEmpty(line) OrElse line.Trim.Length = 0 OrElse _
                  (line.TrimStart.StartsWith("if (") Or _
                   line.TrimStart.StartsWith("if(") Or _
                   line.TrimStart.StartsWith("foreach (") Or _
                   line.TrimStart.StartsWith("foreach(") Or _
                   line.Trim = "{" Or line.Trim = "}" Or _
                   line.Trim.EndsWith("_")) Then
            ret &= line & Strings.nl
         Else
            If Not line.TrimEnd.EndsWith(";") Then
               ret &= line & ";" & Strings.nl
            End If
         End If
         Return ret
      End Function
#End Region

   End Module
End Namespace
